<?php
declare(strict_types=1);



use ErrorException;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Exception\Exception;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Query;
use MongoDB\Driver\WriteConcern;
use WebApp\lib\Config;

/**
 * Class Collection
 * @package txz\common\third_lib\model\lib
 */
class Collection extends AbstractTable {
    private $concern = null;

    /**
     * @param $dbName
     * @param $tableName
     * @param $pre
     * @param $suffix
     * @return mixed
     */
    public function process($dbName, $tableName, $pre = '', $suffix = ''):void
    {
        $this->setPre($pre);
        $this->setSuffix($suffix);
        $this->setDbName($dbName);
        $this->setTableName(($pre ? $pre . '_' : '') . $tableName . ($suffix ? '_' . $suffix : ''));
        $this->setConcern(new WriteConcern(WriteConcern::MAJORITY, 1000));
    }


    /**
     * @param $id
     * @param array $arr
     * @return bool
     */
    public function insertById($id, array $arr)
    {
        try {
            if (!$id)
                throw new ErrorException('no id');

            $arr["_id"] = $id;

            return $this->insert($arr);
        } catch (ErrorException $e) {
            tracker(E, $e->getMessage());
            return false;
        }
    }

    /**
     * @param $arr
     * @return array|int
     */
    public function insert(array $arr)
    {
        $bulk = new BulkWrite;
        $bulk->insert($arr);
        return $this->excute($bulk);
    }

    /**
     * @param $need
     * @return array
     */
    public static function arr2need($need)
    {
        $option = [];
        foreach ($need as $val)
            $option[$val] = 1;
        $option["_id"] = 0;
        $options = [
            'projection' => $option
        ];
        return $options;
    }


    /**
     * @param array $filter
     * @param array $options
     * @return array|bool
     */
    public function find($filter = [], $options = [])
    {
        try {
            $query = new Query($filter, $options);
            $cursor = $this->getCon()->executeQuery($this->getDbName() . "." . $this->getTableName(), $query);
            if ($cursor)
                return $cursor->toArray();
        } catch (Exception $e) {
            tracker(E, $e->getMessage());
        }
        return false;
    }

    /**
     * @param $filter
     * @param int $limit
     * @return array|int
     */
    public function remove($filter, $limit = 1)
    {
        $bulk = new BulkWrite;
        $bulk->delete($filter, ['limit' => $limit]);
        return $this->excute($bulk);
    }

    /**
     * @param $bulk
     * @return bool
     */
    private function excute($bulk): bool
    {
        try{
            $res = $this->getCon()->executeBulkWrite($this->getDbName() . "." . $this->getTableName(), $bulk, $this->getConcern());
            if($res->getWriteConcernError()){
                $this->handle_db_error($res->getWriteConcernError());
                return false;
            }else{
                if ($res->getWriteErrors()){
                    throw new ErrorException($res->getWriteErrors());
                }else{
                    return true;
                }
            }
        }catch (ErrorException $e){
            tracker(E, $e->getMessage());
            return false;
        }
    }
    /**
     * @param array $filter
     * @param $arr
     * @param array $multi_upsert
     * @param string $opt
     * @return array|int
     */
    public function update($filter = [], $arr, $multi_upsert = [false, false], $opt = '$set')
    {
        $bulk = new BulkWrite;
        $bulk->update(
            $filter,
            [$opt => $arr],
            ['multi' => $multi_upsert[0], 'upsert' => $multi_upsert[1]]
        );
        return $this->excute($bulk);
    }


    /**
     * @param $e
     */
    private function handle_db_error($e)
    {
        var_dump($e);
    }

    /**
     * @return Manager
     */
    function getCon(): Manager
    {
        return DbMgr::get($this);
    }

    /**
     * @param $key
     * @return mixed
     */
    function buildConnection($key)
    {
        $config = Config::getConf('tao', 'mongo');
        return new Manager("mongodb://".$config['host'].":".$config['port']);
    }

    /**
     * @return null
     */
    public function getConcern()
    {
        return $this->concern;
    }

    /**
     * @param null $concern
     */
    public function setConcern($concern)
    {
        $this->concern = $concern;
    }
}